//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System;
using System.Collections.ObjectModel;
using Microsoft.Research.ScientificWorkflow.TridentUtilities;

namespace Microsoft.Research.ScientificWorkflow.TridentComposer
{
    /// <summary>
    /// Proxy class which gets references to assemblies in a separate appdomain.
    /// </summary>
    public class BuildReferencesProxy:MarshalByRefObject
    {
        #region private fields
        private Collection<FileReference> fileReferences;
        private Collection<string> selectedDlls;
        private string directoryPath;
        #endregion

        #region constructors
        /// <summary>
        ///  Dummy constructor. Needed to create this object by CreateInstanceAndUnWrap method.
        /// </summary>
        public BuildReferencesProxy()
        {
        }

        /// <summary>
        /// Initializes new instance of the proxy.
        /// </summary>
        /// <param name="fileReferences">The file references.</param>
        /// <param name="selectedDlls">The DLLs whose references have to be found.</param>
        /// <param name="directoryPath">The directory path of the references..</param>
        public BuildReferencesProxy(Collection<FileReference> fileReferences, Collection<string> selectedDlls, string directoryPath)
        {
            this.fileReferences = fileReferences;
            this.selectedDlls = selectedDlls;
            this.directoryPath = directoryPath;
        } 
        #endregion

        #region public methods
        /// <summary>
        /// Build references of the assemblies.
        /// </summary>
        /// <returns>Collection of file references.</returns>
        public Collection<FileReference> Execute()
        {
            AppDomain childAppdomain = null;
            try
            {
                childAppdomain = this.CreateAppDomain();

                BuildReferencesProxy importProxy = childAppdomain.CreateInstanceAndUnwrap
                    ("Microsoft.Research.ScientificWorkflow.TridentComposer",
                    "Microsoft.Research.ScientificWorkflow.TridentComposer.BuildReferencesProxy") as BuildReferencesProxy;
                importProxy.Init(this.fileReferences, this.selectedDlls, this.directoryPath);
                return importProxy.GetReferences();
            }
            finally
            {
                UnloadAppDomain(childAppdomain);
            }
        } 
        #endregion

        #region private methods
        /// <summary>
        /// Initialize the new object created in another appdomain.
        /// </summary>
        /// <param name="fileReferences">The file references.</param>
        /// <param name="selectedDlls">The dll paths.</param>
        /// <param name="directoryPath">The directory path of the references.</param>
        private void Init(Collection<FileReference> fileReferences, Collection<string> selectedDlls, string directoryPath)
        {
            this.fileReferences = fileReferences;
            this.selectedDlls = selectedDlls;
            this.directoryPath = directoryPath;
        }

        /// <summary>
        /// Get the references of the assemblies.
        /// </summary>
        /// <returns>The references.</returns>
        private Collection<FileReference> GetReferences()
        {
            foreach (string selectedDll in this.selectedDlls)
            {
                Helper.BuildReferences(this.fileReferences, selectedDll, this.directoryPath);
            }
            return this.fileReferences;
        }

        /// <summary>
        /// Creates a new app domain.
        /// </summary>
        /// <returns>New instance of the app domain.</returns>
        private AppDomain CreateAppDomain()
        {
            AppDomain newDomain = AppDomain.CreateDomain("BuildReferencesAppdomain");
            newDomain.UnhandledException += new UnhandledExceptionEventHandler(AppDomainUnhandledException);
            return newDomain;
        }

        /// <summary>
        /// Unloads the app domain.
        /// </summary>
        /// <param name="appDomain">Instance of the AppDomain.</param>
        private static void UnloadAppDomain(AppDomain appDomain)
        {
            if (appDomain != null)
            {
                AppDomain.Unload(appDomain);
            }
        }

        /// <summary>
        /// Method logs the unhandled exception in the app domain.
        /// </summary>
        /// <param name="sender">Object who raises the unhandled exception.</param>
        /// <param name="e">Event argument containing exception details.</param>
        private void AppDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            // No-op.
        } 
        #endregion
    }
}
